/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of the Maxsi Library.

	Maxsi Library is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi Library is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
	License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Library. If not, see <http://www.gnu.org/licenses/>.

	Maxsi Library
	A powerful Cross-Platform C++ General Purpose Library that allows you to
	efficiently create high-performance and reliable applications.

	MaxsiWindowGTK.cpp
	Implements the Maxsi Window System using GTK.

******************************************************************************/

#include "MaxsiLibrary.h"

#ifdef Maxsi_Support_GTK

BeginMaxsiNamespace

int CriticalMsgBox(MaxsiHandle Parent, int Flags, MESTR* ErrorMessage, MESTR* ErrorTitle)
{
	if ( gtk_init_check(NULL, NULL) )	
	{
		MAXSI_TODO("There is no support for char* here, only wchar_t*!");

		// Create an error dialog.
		GtkWidget* Dialog = gtk_message_dialog_new((GtkWindow*)NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%S", ErrorTitle);

		if ( Dialog == NULL ) { return 0; }

		MAXSI_TODO("There is no support for char* here, only wchar_t*!");

		gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(Dialog), "%S", ErrorMessage);

		// Display the error dialog and wait for it to close.
		gtk_dialog_run(GTK_DIALOG(Dialog));

		// Delete the error dialog when it has been closed.
		gtk_widget_destroy(Dialog);
		return 1;
	}
	else
	{
		PrintOutput("Critical Error: Could not initialize GTK+!\n");
		return 0;
	}
}

void WindowDestroyed(GtkWidget *widget,  gpointer Data)
{
	if ( Data != NULL )
	{
		MaxsiWindow*	Window	=	(MaxsiWindow*)Data;

		if ( Window != NULL )
		{
			MaxsiWindowEvent	Event;
	
			Event.Owner		=	Window;
			Event.Type		=	MAXSI_EVENT_WINDOW_DESTROYED;

			Window->OnEvent(&Event);
		}
	}
}

//=============================================================================
//	Implement MaxsiWindowSystem as MaxsiWindowSystemGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiWindowSystemGTK, MaxsiWindowSystem, MAXSI_INTERFACE_STATIC);

gboolean MaxsiWindowSystemGTK__OnEventTimer(gpointer data)
{
	((MaxsiWindowSystemGTK*)data)->OnEventTimer();

	return TRUE; // Keep calling this!
}

MaxsiWindowSystemGTK::MaxsiWindowSystemGTK()
{
	FirstEvent				=	NULL;
	LastEvent				=	NULL;
	EventLock				=	NULL;
}

MaxsiWindowSystemGTK::~MaxsiWindowSystemGTK()
{	
	if ( EventLock != NULL ) { ThreadSystem()->DeleteCriticalSection(EventLock); }
}

void MaxsiWindowSystemGTK::ShutDown()
{
	gtk_main_quit();
}

int MaxsiWindowSystemGTK::Initialize(int* argc, char** argv[])
{
	if ( (EventLock = ThreadSystem()->CreateCriticalSection()) == NULL ) { return 0; }

	if ( gtk_init_check(argc, argv) == FALSE ) { return 0; }

	// Letting the UI poll for events from worker threads every 50th milisecond shouldn't be too expensive, nor too unresponsive.
	if ( g_timeout_add(50, MaxsiWindowSystemGTK__OnEventTimer, (gpointer)this) == 0 ) { return 0; }

	return 1;
}

int MaxsiWindowSystemGTK::MainLoop()
{
	gtk_main();
	return 0;
}

MaxsiWindow* MaxsiWindowSystemGTK::CreateWindow(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	MaxsiWindow*	Result	=	NULL;

	if ( MESTRICMP(ClassName, _MESTR("Window")) == 0 ) { Result = new MaxsiWindowGTK; } else
	if ( MESTRICMP(ClassName, _MESTR("Button")) == 0 ) { Result = new MaxsiButtonGTK; } else
	if ( MESTRICMP(ClassName, _MESTR("TextSingleLine")) == 0 ) { Result = new MaxsiTextSingleLineGTK; } else
	if ( MESTRICMP(ClassName, _MESTR("Label")) == 0 ) { Result = new MaxsiLabelGTK; } else
	if ( MESTRICMP(ClassName, _MESTR("ProgressBar")) == 0 ) { Result = new MaxsiProgressBarGTK; } else
	{ PrintOutput("Attempted to create unknown control type!\n"); }

	if ( Result == NULL )	
	{
		delete Result;
		return NULL;
	}

	Result->WindowSystem = this;

	if ( Parent != NULL )
	{
		Result->WindowNext			=	Parent->WindowFirstChild;
		Parent->WindowFirstChild	=	Result;	
	}
	else
	{
		Result->WindowNext			=	this->WindowFirstChild;
		this->WindowFirstChild		=	Result;	
	}

 	if ( Result->Initialize(Parent, ClassName, Width, Height, Left, Top, Title) != MAXSI_ERROR_SUCCESS )
	{
		if ( Parent != NULL )
		{
			Parent->WindowFirstChild	=	Result->WindowNext;
		}
		else
		{
			this->WindowFirstChild		=	Result->WindowNext;
		}

		delete Result;
		return NULL;	
	}

	return Result;
}

void MaxsiWindowSystemGTK::DeleteWindow(MaxsiWindow* Window)
{
	if ( Window != NULL )
	{
		Window->Destroy();
		delete Window;
	}	
}

int MaxsiWindowSystemGTK::MsgBox(MaxsiHandle Parent, int Flags, MESTR* Message, MESTR* Title)
{
	// Create an error dialog.
	GtkWidget* Dialog = gtk_message_dialog_new((GtkWindow*)NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%S", Title);

	if ( Dialog == NULL ) { return 0; }

	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(Dialog), "%S", Message);

	// Display the error dialog and wait for it to close.
	gtk_dialog_run(GTK_DIALOG(Dialog));

	// Delete the error dialog when it has been closed.
	gtk_widget_destroy(Dialog);

	return 1;
}

MaxsiError MaxsiWindowSystemGTK::PostEventThreaded(MaxsiWindowEvent* Event)
{
	if ( Event->NextEvent != NULL ) { return MAXSI_ERROR_NOT_IMPLEMENTED; }

	BYTE*	NewAttachment		=	(Event->Attachment != NULL) ? new BYTE[Event->AttachmentSize] : NULL;

	if ( Event->Attachment != NULL && NewAttachment == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	if ( Event->Attachment ) { memcpy(NewAttachment, Event->Attachment, Event->AttachmentSize); }

	// Crearte a copy of the event.
	MaxsiWindowEvent*	CopiedEvent		=	new MaxsiWindowEvent;

	if ( CopiedEvent == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	memcpy(CopiedEvent, Event, sizeof(MaxsiWindowEvent));

	// Use the new attachment instead of the old.
	CopiedEvent->Attachment		=	NewAttachment;

	ThreadSystem()->EnterCriticalSection(EventLock);

	if ( FirstEvent == NULL )
	{
		FirstEvent				=	CopiedEvent;
		LastEvent				=	CopiedEvent;
	}
	else
	{
		LastEvent->NextEvent	=	CopiedEvent;
		LastEvent				=	CopiedEvent;
	}

	ThreadSystem()->LeaveCriticalSection(EventLock);

	return MAXSI_ERROR_SUCCESS;
}

void MaxsiWindowSystemGTK::OnEventTimer()
{
	MaxsiWindowEvent*	ReceivedEvent	=	NULL;

	while ( true )
	{
		ThreadSystem()->EnterCriticalSection(EventLock);
	
		if ( FirstEvent != NULL )
		{
			ReceivedEvent	=	FirstEvent;
			FirstEvent		=	ReceivedEvent->NextEvent;
		}
		
		ThreadSystem()->LeaveCriticalSection(EventLock);

		if ( ReceivedEvent == NULL ) { break; }

		ReceivedEvent->Owner->OnEvent(ReceivedEvent);

		if ( ReceivedEvent->Attachment ) { delete[] ReceivedEvent->Attachment; }

		delete ReceivedEvent;

		ReceivedEvent	=	NULL;
	}
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiWindowGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiWindowGTK, MaxsiWindow, MAXSI_INTERFACE_DYNAMIC);

MaxsiWindowGTK::MaxsiWindowGTK()
{
	CurrentProgress			=	0.0f;
	CurrentProgressType		=	0;
}

MaxsiWindowGTK::~MaxsiWindowGTK()
{
	
}

MaxsiError MaxsiWindowGTK::Initialize(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	// Attempt to create a window!
	WindowGTK	= gtk_window_new(GTK_WINDOW_TOPLEVEL);

	// See if the window was successfully created!
	if ( WindowGTK == NULL ) { return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	// GTK+ only accept system locale input for the window title.
	// It is not a critical error if this memory allocation fails.
	char*	WindowTitleSL	=	MESTR2CHAR(Title);	

	// Set the title of the window!
	gtk_window_set_title(GTK_WINDOW(WindowGTK), WindowTitleSL);

	if ( WindowTitleSL != NULL ) { delete[] WindowTitleSL; }

	// Set the size of the window.
	gtk_window_set_default_size(GTK_WINDOW(WindowGTK), Width, Height);
	
	MAXSI_TODO("You cannot control the position of the Window!");

	// Set the position of the window.
	gtk_window_set_position(GTK_WINDOW(WindowGTK), GTK_WIN_POS_CENTER);

	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(Parent);

	if ( ParentWindowGTK != NULL )
	{
		//gtk_widget_set_size_request(WindowGTK, Width, Height);
		//gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);
	}

	WindowParent	=	Parent;

	gtk_window_get_position(GTK_WINDOW(WindowGTK), &WindowLeft, &WindowTop);
	gtk_window_get_size(GTK_WINDOW(WindowGTK), &WindowWidth, &WindowHeight);

	// Create a frame.
	Frame = gtk_fixed_new();

	// Check if that went wrong, and if so, clean up.
	if ( Frame == NULL ) { gtk_widget_destroy(WindowGTK); return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	// Add the frame to the Window.
 	gtk_container_add(GTK_CONTAINER(WindowGTK), Frame);

	// Create a callback when the window is destroyed.
	g_signal_connect_swapped(G_OBJECT(WindowGTK), "destroy", G_CALLBACK(WindowDestroyed), (gpointer)this);

	// Remember the title input.
	SetText(Title);

	// Remember our GTK window handle!
	Window	=	(MaxsiHandle)WindowGTK;

	return MAXSI_ERROR_SUCCESS;	
}

MaxsiError MaxsiWindowGTK::Destroy()
{
	gtk_widget_destroy((GtkWidget*)Frame);
	gtk_widget_destroy((GtkWidget*)WindowGTK);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiWindowGTK::Become(MaxsiWindow* NewSelf)
{
	NewSelf->SetText(Text);
	NewSelf->SetProgress(CurrentProgress, CurrentProgressType);
	NewSelf->Move(Left(), Top(), Width(), Height());

	return MAXSI_ERROR_SUCCESS;	
}

MaxsiError MaxsiWindowGTK::OnEvent(MaxsiWindowEvent* Event)
{
	MaxsiError	Result	=	BaseClass::OnEvent(Event);

	if ( Result != MAXSI_ERROR_UNHANDLED_EVENT ) { return Result; }

	if ( EventHandler ) { return EventHandler(Event, &EventUserData); } else { return MAXSI_ERROR_SUCCESS; }
}

MaxsiError MaxsiWindowGTK::Show()
{
	gtk_widget_show(GTK_WIDGET(WindowGTK));
	gtk_widget_show(GTK_WIDGET(Frame));
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiWindowGTK::Hide()
{
	gtk_widget_hide(GTK_WIDGET(Frame));
	gtk_widget_hide(GTK_WIDGET(WindowGTK));
	return MAXSI_ERROR_SUCCESS;
}

void MaxsiWindowGTK::SetEventHandler(MaxsiWindowEventHandler Handler, BYTE* UserData)
{
	EventHandler	=	Handler;
	EventUserData	=	UserData;
}

MaxsiWindow* MaxsiWindowGTK::CreateChild(MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	return WindowSystem->CreateWindow(this, ClassName, Width, Height, Left, Top, Title);
}

MESTR* MaxsiWindowGTK::GetControlType()
{
	return MAXSI_ERROR_SUCCESS;
}

int MaxsiWindowGTK::Left()
{
	return WindowLeft;
}

int MaxsiWindowGTK::Top()
{
	return WindowTop;	
}

int MaxsiWindowGTK::Width()
{
	return WindowWidth;
}

int MaxsiWindowGTK::Height()
{
	return WindowHeight;
}

int MaxsiWindowGTK::Right()
{
	return Left() + Width();
}

int MaxsiWindowGTK::Bottom()
{
	return Top() + Height();
}

MaxsiError MaxsiWindowGTK::Move(int NewLeft, int NewTop, int NewWidth, int NewHeight)
{
	WindowLeft		=	NewLeft;
	WindowTop		=	NewTop;
	WindowWidth		=	NewWidth;
	WindowHeight	=	NewHeight;

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(WindowParent);

	if ( ParentWindowGTK != NULL )
	{
		gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);
	}	

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiWindowGTK::SetText(MESTR* NewText)
{
	// GTK+ only accept system locale input for the window title.
	char*	WindowTitleSL	=	MESTR2CHAR(NewText);

	if ( WindowTitleSL == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	// Set the title of the window!
	gtk_window_set_title(GTK_WINDOW(WindowGTK), WindowTitleSL);

	delete[] WindowTitleSL;

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiWindowGTK::SetFont(MaxsiHandle NewFont)
{
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiWindowGTK::SetProgress(float Progress, int Type)
{
	CurrentProgress			=	Progress;
	CurrentProgressType		=	Type;

	return MAXSI_ERROR_NOT_SUPPORTED;
}

MESTR* MaxsiWindowGTK::GetText()
{
	return NULL;
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiBaseControlGTK.
//=============================================================================

MaxsiBaseControlGTK::MaxsiBaseControlGTK()
{
	Text					=	NULL;	
	CurrentProgress			=	0.0f;
	CurrentProgressType		=	0;
}

MaxsiBaseControlGTK::~MaxsiBaseControlGTK()
{
	IFDEL(Text);
}

MaxsiError MaxsiBaseControlGTK::Destroy()
{
	gtk_widget_destroy((GtkWidget*)WindowGTK);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiBaseControlGTK::OnEvent(MaxsiWindowEvent* Event)
{
	MaxsiError	Result	=	BaseClass::OnEvent(Event);

	if ( Result != MAXSI_ERROR_UNHANDLED_EVENT ) { return Result; }

	return WindowParent->OnEvent(Event);	
}

MaxsiError MaxsiBaseControlGTK::Show()
{
	gtk_widget_show(GTK_WIDGET(WindowGTK));
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiBaseControlGTK::Hide()
{
	gtk_widget_hide(GTK_WIDGET(WindowGTK));
	return MAXSI_ERROR_SUCCESS;
}

void MaxsiBaseControlGTK::SetEventHandler(MaxsiWindowEventHandler Handler, BYTE* UserData)
{
	EventHandler	=	Handler;
	EventUserData	=	UserData;
}

MaxsiWindow* MaxsiBaseControlGTK::CreateChild(MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	return NULL;
}

int MaxsiBaseControlGTK::Left()
{
	return WindowLeft;
}

int MaxsiBaseControlGTK::Top()
{
	return WindowTop;	
}

int MaxsiBaseControlGTK::Width()
{
	return WindowWidth;
}

int MaxsiBaseControlGTK::Height()
{
	return WindowHeight;
}

int MaxsiBaseControlGTK::Right()
{
	return Left() + Width();
}

int MaxsiBaseControlGTK::Bottom()
{
	return Top() + Height();
}

MaxsiError MaxsiBaseControlGTK::Move(int NewLeft, int NewTop, int NewWidth, int NewHeight)
{
	WindowLeft		=	NewLeft;
	WindowTop		=	NewTop;
	WindowWidth		=	NewWidth;
	WindowHeight	=	NewHeight;

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(WindowParent);

	if ( ParentWindowGTK == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);
	gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiBaseControlGTK::SetProgress(float Progress, int Type)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiBaseControlGTK::SetText(MESTR* NewText)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiBaseControlGTK::SetFont(MaxsiHandle NewFont)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MESTR* MaxsiBaseControlGTK::GetText()
{
	return BuildString(1, Text);
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiButtonGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiButtonGTK, MaxsiWindow, MAXSI_INTERFACE_DYNAMIC);

void MaxsiButtonGTKPressed(GtkWidget* Widget, gpointer Data)
{
	MaxsiWindowEvent	Event;
	
	Event.Owner		=	(MaxsiWindow*)Data;
	Event.Type		=	MAXSI_EVENT_PRESSED;
	Event.LParam	=	0;
	Event.RParam	=	0;

	Event.Owner->OnEvent(&Event);
}

MaxsiButtonGTK::MaxsiButtonGTK()
{

}

MaxsiButtonGTK::~MaxsiButtonGTK()
{

}

MaxsiError MaxsiButtonGTK::Initialize(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	if ( Parent == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(Parent);

	if ( ParentWindowGTK == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	WindowParent	=	Parent;
	WindowLeft		=	Left;
	WindowTop		=	Top;
	WindowWidth		=	Width;
	WindowHeight	=	Height;

	WindowGTK		=	gtk_button_new();

	if ( WindowGTK == NULL ) { return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	Show();

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);
	gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);

	g_signal_connect(G_OBJECT(WindowGTK), "clicked", G_CALLBACK(MaxsiButtonGTKPressed), (gpointer)this);

	// Remember the title input.
	SetText(Title);

	// Remember our GTK window handle!
	Window	=	(MaxsiHandle)WindowGTK;

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiButtonGTK::Become(MaxsiWindow* NewSelf)
{
	NewSelf->SetText(Text);
	NewSelf->Move(Left(), Top(), Width(), Height());

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiButtonGTK::SetText(MESTR* NewText)
{
	if ( Text != NULL ) { delete[] Text; }

	Text	=	BuildString(1, NewText);

	if ( Text == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	char*	TextSL	=	MESTR2CHAR(NewText);

	if ( TextSL == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	gtk_button_set_label(GTK_BUTTON(WindowGTK), TextSL);

	delete[] TextSL;

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiButtonGTK::SetFont(MaxsiHandle NewFont)
{
	return MAXSI_ERROR_SUCCESS;
}

MESTR* MaxsiButtonGTK::GetControlType()
{
	return _MESTR("Button");
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiTextSingleLineGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiTextSingleLineGTK, MaxsiWindow, MAXSI_INTERFACE_DYNAMIC);

void MaxsiTextSingleLineGTKPressed(GtkWidget* Widget, gpointer Data)
{
	MaxsiWindowEvent	Event;
	
	Event.Owner		=	(MaxsiWindow*)Data;
	Event.Type		=	MAXSI_EVENT_PRESSED;
	Event.LParam	=	0;
	Event.RParam	=	0;

	Event.Owner->OnEvent(&Event);
}

MaxsiTextSingleLineGTK::MaxsiTextSingleLineGTK()
{

}

MaxsiTextSingleLineGTK::~MaxsiTextSingleLineGTK()
{

}

MaxsiError MaxsiTextSingleLineGTK::Initialize(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	if ( Parent == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(Parent);

	if ( ParentWindowGTK == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	WindowParent	=	Parent;
	WindowLeft		=	Left;
	WindowTop		=	Top;
	WindowWidth		=	Width;
	WindowHeight	=	Height;

	WindowGTK		=	gtk_entry_new();

	if ( WindowGTK == NULL ) { return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	Show();

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);
	gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);

	// Remember the title input.
	SetText(Title);

	// Remember our GTK window handle!
	Window	=	(MaxsiHandle)WindowGTK;

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiTextSingleLineGTK::Become(MaxsiWindow* NewSelf)
{
	NewSelf->SetText(Text);
	NewSelf->Move(Left(), Top(), Width(), Height());

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiTextSingleLineGTK::SetText(MESTR* NewText)
{
	if ( Text != NULL ) { delete[] Text; }

	Text	=	BuildString(1, NewText);

	if ( Text == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	char*	TextSL	=	MESTR2CHAR(NewText);

	if ( TextSL == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	gtk_entry_set_text(GTK_ENTRY(WindowGTK), TextSL);

	delete[] TextSL;

	return MAXSI_ERROR_SUCCESS;
}

MESTR* MaxsiTextSingleLineGTK::GetText()
{
	const gchar* Text	=	gtk_entry_get_text(GTK_ENTRY(WindowGTK));

	if ( Text == NULL ) { return NULL; }

	return CHAR2MESTR((char*)Text);	
}

MaxsiError MaxsiTextSingleLineGTK::SetFont(MaxsiHandle NewFont)
{
	return MAXSI_ERROR_SUCCESS;
}

MESTR* MaxsiTextSingleLineGTK::GetControlType()
{
	return _MESTR("TextSingleLine");
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiLabelGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiLabelGTK, MaxsiWindow, MAXSI_INTERFACE_DYNAMIC);

void MaxsiLabelGTKPressed(GtkWidget* Widget, gpointer Data)
{
	MaxsiWindowEvent	Event;
	
	Event.Owner		=	(MaxsiWindow*)Data;
	Event.Type		=	MAXSI_EVENT_PRESSED;
	Event.LParam	=	0;
	Event.RParam	=	0;

	Event.Owner->OnEvent(&Event);
}

MaxsiLabelGTK::MaxsiLabelGTK()
{

}

MaxsiLabelGTK::~MaxsiLabelGTK()
{

}

MaxsiError MaxsiLabelGTK::Initialize(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	if ( Parent == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(Parent);

	if ( ParentWindowGTK == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	WindowParent	=	Parent;
	WindowLeft		=	Left;
	WindowTop		=	Top;
	WindowWidth		=	Width;
	WindowHeight	=	Height;

	WindowGTK		=	gtk_label_new(NULL);

	if ( WindowGTK == NULL ) { return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	gtk_label_set_line_wrap(GTK_LABEL(WindowGTK), TRUE);
	gtk_misc_set_alignment(GTK_MISC((GtkWidget*)WindowGTK), 0.0, 0.0);

	Show();

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);
	gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);

	// Remember the title input.
	SetText(Title);

	// Remember our GTK window handle!
	Window	=	(MaxsiHandle)WindowGTK;

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiLabelGTK::Become(MaxsiWindow* NewSelf)
{
	NewSelf->SetText(Text);
	NewSelf->Move(Left(), Top(), Width(), Height());

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiLabelGTK::SetText(MESTR* NewText)
{
	if ( Text != NULL ) { delete[] Text; }

	Text	=	BuildString(1, NewText);

	if ( Text == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	char*	TextSL	=	MESTR2CHAR(NewText);

	if ( TextSL == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	gtk_label_set_text(GTK_LABEL(WindowGTK), TextSL);

	delete[] TextSL;

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiLabelGTK::SetFont(MaxsiHandle NewFont)
{
	return MAXSI_ERROR_SUCCESS;
}

MESTR* MaxsiLabelGTK::GetControlType()
{
	return _MESTR("Label");
}

//=============================================================================
//	Implement MaxsiWindow as MaxsiProgressBarGTK.
//=============================================================================

MAXSI_DEFINE_IMPLEMENTATION(MaxsiProgressBarGTK, MaxsiWindow, MAXSI_INTERFACE_DYNAMIC);

MaxsiProgressBarGTK::MaxsiProgressBarGTK()
{

}

MaxsiProgressBarGTK::~MaxsiProgressBarGTK()
{

}

MaxsiError MaxsiProgressBarGTK::Initialize(MaxsiWindow* Parent, MESTR* ClassName, int Width, int Height, int Left, int Top, MESTR* Title)
{
	if ( Parent == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	// Parent must be a MaxsiWindowGTK
	MaxsiWindowGTK*		ParentWindowGTK		=	dynamic_cast<MaxsiWindowGTK*>(Parent);

	if ( ParentWindowGTK == NULL ) { return MAXSI_ERROR_NOT_SUPPORTED; }

	WindowParent	=	Parent;
	WindowLeft		=	Left;
	WindowTop		=	Top;
	WindowWidth		=	Width;
	WindowHeight	=	Height;

	WindowGTK		=	gtk_progress_bar_new();

	if ( WindowGTK == NULL ) { return MAXSI_ERROR_COULD_NOT_CREATE_WINDOW; }

	Show();

	gtk_widget_set_size_request(WindowGTK, WindowWidth, WindowHeight);
	gtk_fixed_put(GTK_FIXED(ParentWindowGTK->Frame), GTK_WIDGET(WindowGTK), WindowLeft, WindowTop);

	// Remember our GTK window handle!
	Window	=	(MaxsiHandle)WindowGTK;

	return MAXSI_ERROR_SUCCESS;	
}


MaxsiError MaxsiProgressBarGTK::Become(MaxsiWindow* NewSelf)
{
	NewSelf->SetProgress(CurrentProgress, CurrentProgressType);
	NewSelf->Move(Left(), Top(), Width(), Height());

	return MAXSI_ERROR_SUCCESS;	
}

MaxsiError MaxsiProgressBarGTK::SetProgress(float Progress, int Type)
{
	CurrentProgress			=	Progress;
	CurrentProgressType		=	Type;

	if ( Type == MAXSI_PROGRESS_INDETERMINATE )
	{
		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(WindowGTK), 0.999f);
	}
	else
	{
		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(WindowGTK), CurrentProgress);
	}

	return MAXSI_ERROR_SUCCESS;
}

MESTR* MaxsiProgressBarGTK::GetControlType()
{
	return _MESTR("ProgressBar");
}

EndMaxsiNamespace

#endif

